Presentación Grupo 6

MongoDB

Autores: Yasser Aoujil, Joaquín Trillo Escribano , Iván José Alba García



In [1]:
from neo4j.v1 import GraphDatabase
from cassandra.cluster import Cluster
import matplotlib.pyplot as plt
from bson import SON
from pymongo import MongoClient, GEOSPHERE, ASCENDING, DESCENDING
from datetime import datetime
import pandas as pd
import folium
import cufflinks as cf
from folium import plugins
from pandas.plotting import scatter_matrix
import matplotlib.pyplot as plt
from sklearn import model_selection
from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix
from sklearn.metrics import accuracy_score
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.naive_bayes import GaussianNB
from sklearn.svm import SVC
from sklearn.cluster import KMeans
from sklearn import datasets
import numpy as np
cf.set_config_file(world_readable=True,offline=True)
import warnings
warnings.filterwarnings('ignore')
In [14]:
def get_session_mon(db_name):
    """
    :param db_name: Nombre de la base de datos de los incidentes y los distritos
    :return: Objeto de tipo DataBase con una conexión a la base de datos local
    """
    client = MongoClient()
    db = client[db_name]
    return db


def total_per_hour(db):
    """
    Esta función usa el framework de aggregate para obtener el total de incidentes de cada hora
    :param db: referencia a la sesión de bd
    :return: Dataframe composed of two columns, hours of the day and count of total incidents in that hour
    """
    # Primero proyectamos cada documento en otro sacando solo el _id y la hora
    pipeline = [
        {"$project": { "h": { "$hour": "$Date" } }}, # nuevo campo h que ha sido proyecto desde la fecha
        {"$group": {"_id": "$h", "count": {"$sum": 1}}},
        {"$sort": SON([("_id", 1), ("count", 1)])}
    ]
    aggregate_results = db.incidents.aggregate(pipeline)
    df = pd.DataFrame(list(aggregate_results))
    return df


def total_per_category(db):
    """
    Esta función usa el framework de aggregate para obtener el total de cada categoría
    :param db: referencia a la sesión de bd
    :return: Dataframe composed of two columns, categories and count of each category
    """
    #Agrupamos por categorĆ­a y los contamos, y luego ordenamos
    pipeline = [
        {"$group": {"_id": "$Category", "count": {"$sum": 1}}},
        {"$sort": SON([("count", -1), ("_id", -1)])}
    ]
    aggregate_results = db.incidents.aggregate(pipeline)
    df = pd.DataFrame(list(aggregate_results))
    return df


def date_querry_mon(op, sdate, edate):
    """
    :param opr: operador B: between dates, L: less than, G: greater than
    :return: devuelve el filtro de la consulta según lo que se recibe por parÔmetro
    en op
    """
    switcher = {
        'B': {"Date": {"$gte": sdate, "$lte": edate}},  # Between
        'GE': {"Date": {"$gte": sdate}},  # Greater than or equal
        'LE': {"Date": {"$lte": sdate}},  # less than or equal
    }
    return switcher.get(op)


def generic_date_search_mon(db, op, sdate, *args, **kwargs):
    """
    :param db: referencia a la sesión de bd
    :param op: operador para seleccionar
    :param sdate: primera fecha, mandaterio
    :param args:
    :param kwargs: Contiene argumento opcional de la seguna fecha
    :return: incidentes que cumplen con el filtro sobre fechas
    """
    edate = kwargs.get('edate', None)
    if not edate is None:
        query = date_querry_mon(op, sdate, edate)
    else:
        query = date_querry_mon(op, sdate, None)
    query_results = db.incidents.find(query)
    df = pd.DataFrame(list(query_results))
    return df


def since_february_mon(db):
    """
    :param db: referencia a la sesión de la bd
    :return: Devuelve los incidentes que han ocurrido desde febrero de 2018
    """
    fecha = datetime(2018, 2, 1)
    incidents = db.incidents.find({"Date": {"$gt": fecha}})
    df = pd.DataFrame(list(incidents))
    return df


def draw_map(ds, save_file=None):
    """
    Esta función recibe un conjunto de incidentes y los dibuja en un mapa usando el paquete folium,
    el mapa se guarda en un fichero.
    :param ds: dataset de incidentes en formato de DataFrame
    """
    incid_map = folium.Map(location=[37.7617007179518, -122.42158168136999], zoom_start=11, tiles='Stamen Terrain')
    marker_cluster = plugins.MarkerCluster().add_to(incid_map)
    for name, row in ds.iterrows():
        folium.Marker([row["Y"], row["X"]], popup=row["Descript"]).add_to(marker_cluster)
    if save_file is True:
        folium.Marker([37.7883306, -122.4447861], popup='Centro del perĆ­metro').add_to(incid_map)
        incid_map.save('incidents.html')
    return incid_map


def draw_heatmap(df):
    """
    Esta función recibe un conjunto de incidentes y los dibuja en un mapa de calor que
    se guarda en un fichero html
    :param ds: dataset de incidentes en formato de DataFrame
    """
    heat_map = folium.Map(location=[37.7617007179518, -122.42158168136999], zoom_start=11, tiles='Stamen Terrain')
    heat_map.add_child(plugins.HeatMap([[row["Y"], row["X"]] for name, row in df.iterrows()]))
    return heat_map


def total_mes(db):
    # Primero proyectamos cada documento en otro sacando solo el _id y la hora
    pipeline = [
        {"$project": { "m": { "$month": "$Date" } }}, # nuevo campo m que ha sido proyecto desde la fecha
        {"$group": {"_id": "$m", "count": {"$sum": 1}}},
        {"$sort": SON([("_id", 1), ("count", 1)])}
    ]
    aggregate_results = db.incidents.aggregate(pipeline)
    df = pd.DataFrame(list(aggregate_results))
    return df


def geo_within(db):
    """
    :param db: referencia a la sesión de la bd
    :return: Esta función obtiene los incidentes que estÔn a una distancia mÔximo de 1000 metros
    desde un punto representado por coordinadas geogrƔficas en formato geojson
    """
    # Montamos la query
    query_incidents = {
        "Location" :{
            "$near": {
                "$geometry" : SON([
                    ("type", "Point"),
                    ("coordinates", [-122.4447861, 37.7883306])
                ]),
                "$maxDistance": 1000,
                "$minDistance": 500
            }
        }
    }
    # Ejecutamos la querry sobre la colección de incidencias
    query_results = db.incidents.find(query_incidents)
    df = pd.DataFrame(list(query_results))
    return df

Pruebas

In [3]:
sfdb_mon = get_session_mon("san_francisco_incidents")

Histograma

In [4]:
# Obtenemos el total de incidentes por cada hora y lo mostramos en un diagrama de barras
dfh_mon = total_per_hour(sfdb_mon)
dfh_mon = dfh_mon.rename(index=str, columns={"_id": "Hour", "count": "Total Incidentes"})
dfh_mon.head()
dfh_mon.iplot(kind='bar', filename='cufflinks/bar-chart-row')
In [5]:
# Obtenemos el total de incidentes por categorĆ­a y lo mostramos en un mapa de tipo PIE
df_mon = total_per_category(sfdb_mon)
df_mon.head()
df_mon.iplot(kind="pie", labels="_id", values="count")
In [6]:
# Fechas para filtro de fechas
fecha1_mon = datetime(2017, 12, 20)
fecha2_mon = datetime(2017, 12, 31)
# Buscaremos los incidentes entre dos fechas, B=Between (ver doc de la función)
date_results_mon = generic_date_search_mon(sfdb_mon, 'B', fecha1_mon, edate=fecha2_mon)
# Buscamos todos los incidentes de febrero
feb_mon = since_february_mon(sfdb_mon)  # Consulta especĆ­fica a una fecha
# Generamos dos mapas con los miles primeros incidentes
m_mon = draw_map(feb_mon.iloc[:1000])
hm_mon = draw_heatmap(date_results_mon.iloc[:1000])
In [7]:
m_mon
Out[7]:
In [8]:
hm_mon
Out[8]:

Incidentes en un perímetro

In [9]:
df_geo = geo_within(sfdb_mon)
df_geo.__len__()
Out[9]:
30836
In [15]:
map_within = draw_map(df_geo, True)

Mapa coroplético

In [11]:
fecha_2018 = datetime(2018, 1, 1)
incidentes_2018 = generic_date_search_mon(sfdb_mon,'GE', fecha_2018)
incidentes_2018.__len__()
Out[11]:
18112
In [12]:
# Calculamos el total de incidentes por distrito
total_incid = pd.DataFrame(incidentes_2018['PdDistrict'].value_counts().astype(float))
total_incid = total_incid.reset_index()
total_incid.columns = ['Distrito', 'TotalIncidentes']
mapa_coropletico = folium.Map(location=[37.7617007179518, -122.42158168136999], zoom_start=11, tiles='Stamen Terrain')
mapa_coropletico.choropleth(geo_data='pddistricts.geojson',
             data=total_incid,
             columns=['Distrito', 'TotalIncidentes'],
             key_on='feature.properties.district',
             fill_color='YlOrRd',
             fill_opacity = 0.7, 
             line_opacity = 0.2,
             highlight=True,
             legend_name = 'NĆŗmero de incidentes por distrito')
In [13]:
# Mostrar el mapa
mapa_coropletico
Out[13]: